package com.pine.dbant.core.config;


import com.pine.dbant.common.exeception.BusinessException;
import com.pine.dbant.core.model.ProfileType;
import com.pine.dbant.core.model.SystemErrorCode;
import com.pine.sunflower.core.model.CommonErrorCode;
import com.pine.sunflower.core.model.ErrorCode;
import com.pine.sunflower.core.model.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.internal.engine.path.PathImpl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.remoting.RemoteConnectFailureException;
import org.springframework.remoting.RemoteLookupFailureException;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Description: 统一异常处理器
 * @Author: xiaoyuan
 * @Date: 2022/4/6 15:57
 */
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@Slf4j
public class ExceptionMessageAdvice {

    @Value("${spring.profiles.active:dev}")
    public String activeProfile;

    private String getMessage(Exception exception, ErrorCode status) {
        if (ProfileType.DEV.getCode().equalsIgnoreCase(activeProfile)
                || ProfileType.TEST.getCode().equalsIgnoreCase(activeProfile)) {
            return StringUtils.isNotBlank(exception.getMessage()) ? exception.getMessage() : status.getDescription();
        }
        return status.getDescription();

    }

    /**
     * 构建返回错误
     *
     * @param status
     * @param exception
     * @param data
     * @return
     */
    private Result failure(HttpServletRequest request, ErrorCode status, Exception exception,
                           boolean showFriendlyMessage, Object data) {
        String meesage = status.getDescription();
        if (exception != null && !showFriendlyMessage) {
            //根据是否是开发或者测试模式，改变提示消息
            meesage = this.getMessage(exception, status);
        }
        return Result.error(status.getCode(), meesage);
    }


    private Result exceptionHandler(HttpServletRequest request, ErrorCode status,  Exception e,
                                    boolean showFriendlyMessage, Object data) {
        //  if (httpStatus.value() != HttpStatus.OK.value()) {
        log.error(status.getDescription() + ":" + e.getMessage(), e);
        //  }
        return failure(request, status, e, showFriendlyMessage, data);
    }
    private Result exceptionHandler(HttpServletRequest request, ErrorCode status,  Exception e, Object data) {
      return   exceptionHandler(request,status,e,true,data);
    }

    private Result exceptionHandler(HttpServletRequest request, ErrorCode status, Exception e, boolean showFriendlyMessage) {
        return exceptionHandler(request, status, e, showFriendlyMessage, null);
    }

    /**
     * @Description: 数据错误
     * @Author: xyjxust
     * @Date: 2019/1/24 11:13
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleMethodArgumentNotValidException(HttpServletRequest request, MethodArgumentNotValidException e) {
        FieldError fieldError = e.getBindingResult().getFieldError();
        return exceptionHandler(request, SystemErrorCode.REQUEST_PARAM_ERROR,
                new BusinessException(CommonErrorCode.REQUEST_PARAM_ERROR.getCode(),
                        fieldError == null ? CommonErrorCode.REQUEST_PARAM_ERROR.getDescription() : fieldError.getDefaultMessage()), false);
    }

    /**
     * @Description: 数据错误
     * @Author: xyjxust
     * @Date: 2019/1/24 11:13
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(ConstraintViolationException.class)
    public Result handleValidationException(HttpServletRequest request, ConstraintViolationException e) {
        List<Map<String, Object>> fields = new ArrayList<>();
        for (ConstraintViolation<?> cv : e.getConstraintViolations()) {
            String fieldName = ((PathImpl) cv.getPropertyPath()).getLeafNode().asString();
            String message = cv.getMessage();
            Map<String, Object> field = new HashMap<>();
            field.put("field", fieldName);
            field.put("message", message);
            fields.add(field);
        }
        Map<String, Object> map = fields.get(0);
        return exceptionHandler(request, CommonErrorCode.REQUEST_PARAM_ERROR,
                new BusinessException(CommonErrorCode.REQUEST_PARAM_ERROR.getCode(),
                        "" +
                                map.get("message")), false, fields);
    }

    /**
     * @Description: 数据错误
     * @Author: xyjxust
     * @Date: 2019/1/24 11:13
     */
    @ResponseBody
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result handleBindException(HttpServletRequest request, BindException e) {
        List<Map<String, Object>> fields = new ArrayList<>();
        for (FieldError error : e.getFieldErrors()) {
            Map<String, Object> field = new HashMap<>();
            field.put("field", error.getField());
            field.put("message", error.getDefaultMessage());
            fields.add(field);
        }
        Map<String, Object> map = fields.get(0);
        return exceptionHandler(request, CommonErrorCode.REQUEST_PARAM_ERROR,
                new BusinessException(CommonErrorCode.REQUEST_PARAM_ERROR.getCode(),
                        "" + map.get("message")), false, fields);
    }


    /**
     * @Description: 缺少参数
     * @Author: xyjxust
     * @Date: 2019/1/24 11:13
     */
    @ResponseBody
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result handleIllegalArgumentException(HttpServletRequest request, IllegalArgumentException e) {
        return exceptionHandler(request,  CommonErrorCode.REQUEST_PARAM_ERROR, e, true);
    }


    /**
     * @Description: 缺少参数
     * @Author: xyjxust
     * @Date: 2019/1/24 11:13
     */
    @ResponseBody
    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result handleMissingServletRequestParameterException(HttpServletRequest request,
                                                                MissingServletRequestParameterException e) {
        return exceptionHandler(request,  CommonErrorCode.REQUEST_PARAM_ERROR, e, true);
    }

    /**
     * @Description: 缺少参数
     * @Author: xyjxust
     * @Date: 2019/1/24 11:13
     */
    @ResponseBody
    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result HttpMessageNotReadableException(HttpServletRequest request,
                                                  HttpMessageNotReadableException e) {
        return exceptionHandler(request,  CommonErrorCode.REQUEST_PARAM_ERROR,  e, true);
    }

    /**
     * @Description: 参数类型错误
     * @Author: xyjxust
     * @Date: 2019/1/24 11:13
     */
    @ResponseBody
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result handleMethodArgumentTypeMismatchExceptionException(HttpServletRequest request,
                                                                     MethodArgumentTypeMismatchException e) {
        return exceptionHandler(request, CommonErrorCode.REQUEST_PARAM_ERROR,  e, true);
    }

    /**
     * @Description: 未知错误
     * @Author: xyjxust
     * @Date: 2019/1/24 11:12
     */
    @ResponseBody
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result handleException(HttpServletRequest request, Exception e) {
        return exceptionHandler(request, CommonErrorCode.SYSTEM_ERROR,  e, true);
    }

    /**
     * @Description: 不支持的方法
     * @Author: xyjxust
     * @Date: 2019/1/24 11:12
     */
    @ResponseBody
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    public Result handleHttpRequestMethodNotSupportedException(HttpServletRequest request, HttpRequestMethodNotSupportedException e) {
        return exceptionHandler(request, CommonErrorCode.REQUEST_ERROR,  e, true);
    }

    /**
     * @Description: 业务错误
     * @Author: xyjxust
     * @Date: 2019/1/24 11:04
     */
    @ResponseBody
    @ExceptionHandler({BusinessException.class})
    @ResponseStatus(HttpStatus.OK)
    public Result handleServiceException(BusinessException e) {
        log.warn("业务错误:" + e.getMessage());
        if (ProfileType.DEV.getCode().equalsIgnoreCase(activeProfile)
                || ProfileType.TEST.getCode().equalsIgnoreCase(activeProfile)) {
            e.printStackTrace();
        }
        if (StringUtils.isEmpty(e.getCode())) {
            return Result.error(CommonErrorCode.SYSTEM_ERROR.getCode(), e.getMessage());
        }
        return Result.error(e.getCode(), e.getMessage());

    }

    /**
     * 远程调用失败
     */
    @ResponseBody
    @ExceptionHandler(RemoteLookupFailureException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result handleRemoteLookupFailureException(HttpServletRequest request, RemoteLookupFailureException e) {
        return exceptionHandler(request, SystemErrorCode.NETWORK_ERROR, e, true);
    }

    /**
     * 远程调用失败
     */
    @ResponseBody
    @ExceptionHandler(RemoteConnectFailureException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result RemoteConnectFailureException(HttpServletRequest request, RemoteConnectFailureException e) {
        return exceptionHandler(request, SystemErrorCode.NETWORK_ERROR,  e, true);
    }
}
